﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip;

namespace BoYuan.Framework.Uitility
{
    //https://www.cnblogs.com/yuangang/p/5581391.html

    public class ZipHelper
    {
        #region 压缩

        /// <summary>
        /// ZIP:压缩单个文件
        /// </summary>
        /// <param name="FileToZip">需要压缩的文件（绝对路径）</param>
        /// <param name="ZipedPath">压缩后的文件路径（绝对路径）</param>
        /// <param name="ZipedFileName">压缩后的文件名称（文件名，默认 同源文件同名）</param>
        /// <param name="CompressionLevel">压缩等级（0 无 - 9 最高，默认 5）</param>
        /// <param name="BlockSize">缓存大小（每次写入文件大小，默认 2048）</param>
        /// <param name="password">加密密码(为null或空，不加密)</param>
        public static void ZipFileOne(string FileToZip, string ZipedPath, string ZipedFileName = "", int CompressionLevel = 5, int BlockSize = 2048, string password = "")
        {
            //如果文件没有找到，则报错
            if (!System.IO.File.Exists(FileToZip))
            {
                throw new System.IO.FileNotFoundException("指定要压缩的文件: " + FileToZip + " 不存在!");
            }

            //文件名称（默认同源文件名称相同）
            string ZipFileName = string.IsNullOrEmpty(ZipedFileName) ? ZipedPath + "\\" + new FileInfo(FileToZip).Name.Substring(0, new FileInfo(FileToZip).Name.LastIndexOf('.')) + ".zip" : ZipedPath + "\\" + ZipedFileName + ".zip";

            using (System.IO.FileStream ZipFile = System.IO.File.Create(ZipFileName))
            {
                using (ZipOutputStream ZipStream = new ZipOutputStream(ZipFile))
                {
                    using (System.IO.FileStream StreamToZip = new System.IO.FileStream(FileToZip, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                    {
                        string fileName = FileToZip.Substring(FileToZip.LastIndexOf("\\") + 1);

                        ZipEntry ZipEntry = new ZipEntry(fileName);

                        if (!string.IsNullOrEmpty(password))
                        {
                            ZipStream.Password = password;//压缩文件加密
                        }

                        ZipStream.PutNextEntry(ZipEntry);

                        //设置压缩级别
                        ZipStream.SetLevel(CompressionLevel);

                        //缓存大小
                        byte[] buffer = new byte[BlockSize];

                        int sizeRead = 0;

                        try
                        {
                            do
                            {
                                sizeRead = StreamToZip.Read(buffer, 0, buffer.Length);
                                ZipStream.Write(buffer, 0, sizeRead);
                            }
                            while (sizeRead > 0);
                        }
                        catch (System.Exception ex)
                        {
                            throw ex;
                        }

                        StreamToZip.Close();
                    }

                    ZipStream.Finish();
                    ZipStream.Close();
                }

                ZipFile.Close();
            }
        }

        /// <summary>
        /// 批量压缩
        /// </summary>
        /// <param name="filePathList">文件路径列表</param>
        /// <param name="zipedPath"></param>
        /// <param name="isHaveDivPath">是否保留压缩路径</param>
        /// <param name="basePath">isHaveDivPath为true时候才可用,替换基本路径,例:D:/123/abc/1.txt ,basePath值为D:/123 则 压缩路径是abc/1.txt</param>
        /// <param name="password">加密密码(为null或空，不加密)</param>
        public static void ZipFileList(List<string> filePathList, string zipedPath, bool isHaveDivPath = true, List<string> basePath = null, string password = null)
        {
            //https://www.cnblogs.com/colder/archive/2011/04/07/2008185.html
            using (ZipFile zip = ICSharpCode.SharpZipLib.Zip.ZipFile.Create(zipedPath))
            {
                zip.Password = password;
                zip.BeginUpdate();
                if (isHaveDivPath)
                {
                    if (basePath == null || basePath.Count == 0)
                    {
                        foreach (var path in filePathList)
                        {
                            if (File.Exists(path))
                                zip.Add(path);
                        }
                    }
                    else
                    {
                        string entryName;
                        foreach (var path in filePathList)
                        {
                            if (File.Exists(path))
                            {
                                zip.Add(path, ReplaceStringEmptyOfList(path, basePath));
                            }

                        }
                    }
                }
                else
                {
                    foreach (var path in filePathList)
                    {
                        if (File.Exists(path))
                            zip.Add(path, Path.GetFileName(path));
                    }
                }
                zip.CommitUpdate();
            }
        }

        /// <summary>
        /// 批量替换字符串
        /// </summary>
        /// <param name="path"></param>
        /// <param name="basePath"></param>
        /// <returns></returns>
        private static string ReplaceStringEmptyOfList(string path, List<string> basePath)
        {
            StringBuilder sb = new StringBuilder(path);
            foreach (var bPath in basePath)
            {
                sb.Replace(bPath, string.Empty);
            }

            return sb.ToString();
        }

        /// <summary>
        /// ZIP：压缩文件夹
        /// </summary>
        /// <param name="DirectoryToZip">需要压缩的文件夹（绝对路径）</param>
        /// <param name="ZipedPath">压缩后的文件路径（绝对路径）包含文件名</param>
        /// <param name="password">加密密码(为null或空，不加密)</param>
        public static bool ZipDirectory(string DirectoryToZip, string ZipedPath, string password = "")
        {
            try
            {
                //如果目录不存在，则报错
                if (!Directory.Exists(DirectoryToZip))
                {
                    throw new FileNotFoundException("指定的目录: " + DirectoryToZip + " 不存在!");
                }


                using (FileStream ZipFile = File.Create(ZipedPath))
                {
                    using (ZipOutputStream s = new ZipOutputStream(ZipFile))
                    {
                        if (!string.IsNullOrEmpty(password))
                        {
                            s.Password = password;//压缩文件加密
                        }
                        ZipSetp(DirectoryToZip, s, "");
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }
        /// <summary>
        /// 递归遍历目录
        /// </summary>
        private static void ZipSetp(string strDirectory, ZipOutputStream s, string parentPath)
        {
            if (strDirectory[strDirectory.Length - 1] != Path.DirectorySeparatorChar)
            {
                strDirectory += Path.DirectorySeparatorChar;
            }
            Crc32 crc = new Crc32();

            string[] filenames = Directory.GetFileSystemEntries(strDirectory);

            foreach (string file in filenames)// 遍历所有的文件和目录
            {

                if (Directory.Exists(file))// 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
                {
                    string pPath = parentPath;
                    pPath += file.Substring(file.LastIndexOf("\\") + 1);
                    pPath += "\\";
                    ZipSetp(file, s, pPath);
                }

                else // 否则直接压缩文件
                {
                    //打开压缩文件
                    using (FileStream fs = File.OpenRead(file))
                    {

                        byte[] buffer = new byte[fs.Length];
                        fs.Read(buffer, 0, buffer.Length);

                        string fileName = parentPath + file.Substring(file.LastIndexOf("\\") + 1);
                        ZipEntry entry = new ZipEntry(fileName);

                        entry.DateTime = DateTime.Now;
                        entry.Size = fs.Length;

                        fs.Close();

                        crc.Reset();
                        crc.Update(buffer);

                        entry.Crc = crc.Value;
                        s.PutNextEntry(entry);

                        s.Write(buffer, 0, buffer.Length);
                    }
                }
            }
        }

        #endregion

        #region 解压
        /// <summary>
        /// ZIP:解压一个zip文件
        /// </summary>
        /// <param name="ZipFile">需要解压的Zip文件（绝对路径）</param>
        /// <param name="TargetDirectory">解压到的目录</param>
        /// <param name="Password">解压密码</param>
        /// <param name="OverWrite">是否覆盖已存在的文件</param>
        public static void UnZip(string ZipFile, string TargetDirectory, string Password, bool OverWrite = true)
        {
            //如果解压到的目录不存在，则报错
            if (!System.IO.Directory.Exists(TargetDirectory))
            {
                throw new System.IO.FileNotFoundException("指定的目录: " + TargetDirectory + " 不存在!");
            }
            //目录结尾
            if (!TargetDirectory.EndsWith("\\")) { TargetDirectory = TargetDirectory + "\\"; }

            using (ZipInputStream zipfiles = new ZipInputStream(File.OpenRead(ZipFile)))
            {
                zipfiles.Password = Password;
                ZipEntry theEntry;

                while ((theEntry = zipfiles.GetNextEntry()) != null)
                {
                    string directoryName = "";
                    string pathToZip = "";
                    pathToZip = theEntry.Name;

                    if (pathToZip != "")
                        directoryName = Path.GetDirectoryName(pathToZip) + "\\";

                    string fileName = Path.GetFileName(pathToZip);

                    Directory.CreateDirectory(TargetDirectory + directoryName);

                    if (fileName != "")
                    {
                        if ((File.Exists(TargetDirectory + directoryName + fileName) && OverWrite) || (!File.Exists(TargetDirectory + directoryName + fileName)))
                        {
                            using (FileStream streamWriter = File.Create(TargetDirectory + directoryName + fileName))
                            {
                                int size = 2048;
                                byte[] data = new byte[2048];
                                while (true)
                                {
                                    size = zipfiles.Read(data, 0, data.Length);

                                    if (size > 0)
                                        streamWriter.Write(data, 0, size);
                                    else
                                        break;
                                }
                                streamWriter.Close();
                            }
                        }
                    }
                }

                zipfiles.Close();
            }
        }


        #endregion


        /// <summary>
        /// 遍历文件夹，获取所有文件信息
        /// </summary>
        /// <param name="path">文件夹路径</param>
        /// <param name="sb"></param>
        public static void Director(string path, StringBuilder sb)
        {
            DirectoryInfo d = new DirectoryInfo(path);
            FileSystemInfo[] fsinfos = d.GetFileSystemInfos();
            foreach (FileSystemInfo fsinfo in fsinfos)
            {
                if (fsinfo is DirectoryInfo)     //判断是否为文件夹
                {
                    Director(fsinfo.FullName, sb);//递归调用
                }
                else
                {
                    sb.AppendLine(fsinfo.FullName);//输出文件的全部路径
                }
            }
        }

        /*
          //递归文件夹获取所有文件信息
           StringBuilder sb = new StringBuilder();
            Director("d:\\winapp\\bin\\",sb);//递归调用            
            string info = sb.ToString();
         */

    }
}

/*
 //多文件 调用示例
            string basePath = "d:\\winapp\\bin\\";
            string zipPath = "c:\\release";
            List<string> fileList = new List<string>
            {
basePath+"Common.dll",
basePath+"DBServices.dll",
basePath+"WinControl.dll",
            };

            if (!System.IO.Directory.Exists(zipPath))
            {
                System.IO.Directory.CreateDirectory(zipPath);
            }

            try
            {
                ZipHelper.ZipFileList(fileList, zipPath+"\\update.zip",true,new List<string>(){basePath});
                MessageBox.Show("压缩成功请查看");
            }
            catch (Exception ex)
            {
                MessageBox.Show("压缩异常:" + ex.Message);
            }
 */